#include "mhscpu.h"
#include <string.h>
#include <stdio.h>

/* Private function prototypes -----------------------------------------------*/
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

#define BIT(n)          (1UL << (n))
#define MH_SMCU_SYS_CTL_BASE            (0x40000000UL)
//MH_SMCU_SYS_CTRL_TypeDef * pSysCtrl = (MH_SMCU_SYS_CTRL_TypeDef *)MH_SMCU_SYS_CTL_BASE;

typedef struct
{
    volatile uint32_t DHCSR;
    volatile uint32_t DCRSR;
    volatile uint32_t DCRDR;
    volatile uint32_t DEMCR;
} DGB_CTRL, *DGB_CTRL_ID;

//DGB_CTRL_ID pDGB_CTRL = (DGB_CTRL_ID)0xE000EDF0;


#define pSTART      ((uint32_t *)0x20000404UL)
#define pEND        ((uint32_t *)0x20010000UL)


#define NUM         (0xA55A5AA5UL)
#define TESTADDR    (0x15ADE954UL)
typedef union
{
    uint32_t u32Value;
    uint16_t u16Value[2];
    uint8_t  u8Value[4];
} BYTE4;
//volatile uint32_t u32Tmp = 0;
static const BYTE4 byteTest = {NUM};
volatile uint32_t u32Count = 0;
volatile uint32_t u32Times = 2;

void SysTick_Handler(void)
{
    if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)
    {
        if (++u32Count > u32Times)
        {
            u32Count = 1;
            if (0 == (BIT(0) & GPIO_GROUP[3].IODR))
            {
                GPIO_GROUP[3].BSRR = BIT(0);
            }
            else
            {
                GPIO_GROUP[3].BSRR = BIT(0 + 16);
            }
        }
    }
}
/*
uint32_t get_HCLK_1Ms(void)
{
    uint8_t u8Tmp;
    uint32_t u32OneMs;

    u8Tmp = (SYSCTRL->FREQ_SEL >> 4) & 0x03;
    if (0 == u8Tmp)
    {
        u32OneMs = 72000;
    }
    else if (1 == u8Tmp)
    {
        u32OneMs = 96000;
    }

    if (0 != (SYSCTRL->FREQ_SEL & BIT(3)))
    {
        u32OneMs >>= 1;
        if (0 != (SYSCTRL->FREQ_SEL & BIT(2)))
        {
            u32OneMs >>= 1;
        }
    }

    return u32OneMs;
}

uint32_t get_PCLK_1Ms(void)
{
    uint32_t u32OneMs;

    u32OneMs = get_HCLK_1Ms();
    
    if (0 != (SYSCTRL->FREQ_SEL & BIT(1)))
    {
        u32OneMs >>= 1;
        if (0 != (SYSCTRL->FREQ_SEL & BIT(0)))
        {
            u32OneMs >>= 1;
        }
    }

    return u32OneMs;
}
*/
static uint32_t match_Tick(uint32_t u32Count)
{
    uint32_t i;

    for (i = 1; i < ~0UL; i++)
    {
        if (u32Count / i < SysTick_LOAD_RELOAD_Msk && (0 == (u32Count % i)))
        {
            return i;
        }
    }
    GPIO_GROUP[3].BSRR = BIT(0 + 16);
    while (1);
    return 0;
}

int32_t Led_Sparkle(uint32_t u32Freq)
{
    uint32_t u32Real;

    SysTick->CTRL = 0;
    if (u32Freq < 1)
    {
        GPIO_GROUP[3].BSRR = BIT(0 + 16);
        while (1);
    }
    u32Real = (SYSCTRL->HCLK_1MS_VAL * 1000 + u32Freq) / (u32Freq << 1);
    u32Times = match_Tick(u32Real);

    u32Count = 1;
    SysTick->LOAD = u32Real / u32Times;
    SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_CLKSOURCE_Msk;

    while (1);
    return 0;
}

extern int __Vectors;

int main(void)
{
    uint32_t *pu32Start = (uint32_t *)((__Vectors + 4) & ~0x03UL);
    uint32_t *pu32Dest = pu32Start, i;
    uint8_t *pu8Dest;
    uint16_t *pu16Dest;
    uint8_t au32Freq[] = {0x0c, 0x00, 0x10, 0x20, 0x3C, 0x38, 0x30};
    //uint8_t au32Freq[] = {0x0c, 0x00, 0x10, 0x3C, 0x38};
    //uint8_t au32Freq[] = { 0x30 };
    uint8_t u8Count = 0;
    //Enable controllers crypt(4) trng(5) uart0(10) spi0_cg(16) spi0_slv(18) timer01_cg(20) GPIO(21) crc(24).
    SYSCTRL->CG_CTRL1 |= ~0UL;

    //reset controllers crypt(5), uart0(10), trng(14), spi0(15),timer0(17), GPIO(19), crc(20)
    SYSCTRL->SOFT_RST1 = BIT(20);
    
    //pu32Start = (uint32_t *)0x20040000;
    while (u8Count < sizeof(au32Freq))
    {
        //SYSCTRL->FREQ_SEL = au32Freq[u8Count];
//        //Used as GPIO F7
//        GPIO_ALT_GROUP[5] &= ~(0x03UL << 6);
//        GPIO_ALT_GROUP[5] |= 1UL << 6;
        //Output enable
        GPIO_GROUP[3].OEN &= ~BIT(0);
        GPIO_GROUP[3].BSRR = BIT(0 + 16);

        //Set every value unit as NUM.
        pu32Dest = pu32Start;
        while (pu32Dest < pEND)
        {
            *pu32Dest++ = NUM;
        }
        //Check every unit value.
        pu32Dest = pu32Start;
        while (pu32Dest < pEND)
        {
            if (byteTest.u32Value != *pu32Dest++)
            {
                Led_Sparkle(3);
            }
        }
        //Test 8 Bit mask.
        pu8Dest = (uint8_t *)pu32Start;
        while (pu8Dest < (uint8_t *)pEND)
        {
            if (byteTest.u8Value[(uint32_t)pu8Dest&0x03] != *pu8Dest)
            {
                Led_Sparkle(3);
            }
            pu8Dest++;
        }
        //Test 16 Bit mask.
        pu16Dest = (uint16_t *)pu32Start;
        while (pu16Dest < (uint16_t *)pEND)
        {
            if (byteTest.u16Value[((uint32_t)pu16Dest >> 1)&0x01] != *pu16Dest)
            {
                Led_Sparkle(3);
            }
            pu16Dest++;
            
        }

        //Set every value unit as ~NUM.
        pu32Dest = pu32Start;
        while (pu32Dest < pEND)
        {
            *pu32Dest = ~(*pu32Dest);
            pu32Dest++;
        }
        //Check every unit value(~NUM).
        pu32Dest = pu32Start;
        while (pu32Dest < pEND)
        {
            if (~NUM != *pu32Dest++)
            {
                Led_Sparkle(4);
            }
        }
        //Set every value unit as addr(ascend).
        pu32Dest = pu32Start;
        while (pu32Dest < pEND)
        {
            *pu32Dest = (uint32_t)pu32Dest;
            pu32Dest++;
        }
        //Check every unit value(~NUM).
        pu32Dest = pu32Start;
        while (pu32Dest < pEND)
        {
            if ((uint32_t)pu32Dest != *pu32Dest)
            {
                Led_Sparkle(5);
            }
            pu32Dest++;
        }
        //Set every value unit as ~addr(descend).
        pu32Dest = pEND - 1;
        while (pu32Dest >= pu32Start)
        {
            *pu32Dest = ~(*pu32Dest);
            pu32Dest--;
        }
        pu32Dest = pu32Start;
        while (pu32Dest < pEND)
        {
            if (~(*pu32Dest) != (uint32_t)pu32Dest)
            {
                Led_Sparkle(6);
            }
            pu32Dest++;
        }
        u8Count++;
    }
    /*
    while(1)
    {
        for (i = 0; i < 0xFFFFFF;i++);
        if (0 == (BIT(7) & GPIO_PF_IODR))
        {
            GPIO_PF_BSRR = BIT(7);
        }
        else
        {
            GPIO_PF_BSRR = BIT(23);
        }
    }
    */
    Led_Sparkle(1);
    return 0;
}

#ifdef  USE_FULL_ASSERT

/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

    /* Infinite loop */
    while (1)
    {
    }
}
#endif

